package ch.mtb.conwin;

import java.awt.Color;
import java.awt.Graphics;
import java.util.Collections;
import java.util.Vector;

//	**********************************************************************************************************************************

/**
 * A population of genomes, witch knows about the positions of those.
 */
// **********************************************************************************************************************************
public class Population extends World implements Runnable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    private int size;
    private double repRate;

    private Vector<Genome> genomeArray;

    Graphics graphicsContext;
    
    private int _generation=0;
    private boolean _done;

    public Population() {

        super();
        size = 70;
		repRate=0.02;

        // initialising the genomes
        genomeArray = new Vector<Genome>(size);
        for (int i = 0; i < size; i++) {
            //Genome genome = new Genome();
            Genome genome = new Genome();
            
            genomeArray.add(genome);

        }

        // giving a position to each one

        Vector<Integer> randomArray = new Vector<Integer>((int) Math.pow(this
                .getDimensions(), 2));
        for (int i = 0; i < randomArray.size(); i++)
            randomArray.set(i, new Integer(i));

        Collections.shuffle(randomArray);

        // TODO zuf�llig verteilen ..
        // RandomSampler randomSet = new RandomSampler(0, 100, 10, new
        // MersenneTwister());
        // randomSet.nextBlock();
        for (int i = 0; i < size; i++) {
            // Integer xi = randomArray.get(i);
            Genome genome = genomeArray.get(i);
            genome.setX(i);
            genome.setY(i);
            // genomeArray.set(i, genome);

        }
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        for (int i = 0; i < size; i++)
            this.drawGenome(i, g);
    }

    public void drawGenome(int i, Graphics g) {
        Genome genome = genomeArray.get(i);
        try {
            g.setColor(genome.getColor());
            g.fillRect(genome.getX() * squareWidth, squareWidth
                    * (this.getDimensions() - genome.getY() - 1), squareWidth,
                    squareWidth);
        } catch (Exception e) {
            System.err.println("drawing failed");
        }
    }

    public void eraseGenome(int i, Graphics g) {
        Genome genome = genomeArray.get(i);
        try {
            g.setColor(Color.black);
            g.fillRect(genome.getX() * squareWidth, squareWidth
                    * (this.getDimensions() - genome.getY() - 1), squareWidth,
                    squareWidth);
            // g.setColor(Color.white);
            // g.drawRect(genome.getX() * squareWidth ,squareWidth *
            // (this.getDimensions() - genome.getY() -
            // 1),squareWidth,squareWidth);
        } catch (Exception e) {
            System.err.println("erasing failed");
        }
    }

    private Vector<Genome> findNeighbours(int n) {
        int dx, dy;

        Vector<Genome> neighbours = new Vector<Genome>();

        Genome genomeN = genomeArray.get(n);
        for (int i = 0; i < size; i++) {
            Genome genomeI = genomeArray.get(i);

            dx = genomeI.getX() - genomeN.getX();
            dy = genomeI.getY() - genomeN.getY();

            if ((dx > -1) && (dx < 1) && (dy > -1) && (dy < 1))
                neighbours.addElement(genomeArray.get(i)); // verursacht unsafe
            // operation

        }

        // System.out.println(neighbours.toString());

        return neighbours;
    }

    public int fieldOccupiedBy(int x, int y) {
        int occupiedBy = -1; // -1: not occupied
        for (int i = 0; i < genomeArray.size(); i++) {
            Genome candidate = genomeArray.get(i);
            // hier sicherung gegen mehrfachbelegung der felder einbauen:
            // already occupied?

            if ((candidate.getX() == x) && (candidate.getY() == y))
                occupiedBy = i;
        }
        return occupiedBy;
    }

    /**
     * determines which neighbouring fields are occupied and returns the number
     * of the situation as integer
     */

    public int getSituation(int n) {
        int situation = 0;
        int dx, dy;

        Genome genomeN = genomeArray.get(n);
        for (int i = 0; i < size; i++) {
            Genome genomeI = genomeArray.get(i);

            dx = genomeI.getX() - genomeN.getX();
            dy = genomeI.getY() - genomeN.getY();

            switch (dx) {
            case (0):
                if (dy == 1)
                    situation = situation + 2;
                if (dy == -1)
                    situation = situation + 2 ^ 5;
                break;
            case (1):
                if (dy == 1)
                    situation = situation + 2 ^ 2;
                if (dy == 0)
                    situation = situation + 2 ^ 3;
                if (dy == -1)
                    situation = situation + 2 ^ 4;
                break;
            case (-1):
                if (dy == 1)
                    situation = situation + 1;
                if (dy == 0)
                    situation = situation + 2 ^ 7;
                if (dy == -1)
                    situation = situation + 2 ^ 6;
                break;
            }

            // {
            // case(0):
            // if (dy == 1)
            // situation = situation + 2^2;
            // if (dy == -1)
            // situation = situation + 2^6;
            // break;
            // case(1):
            // if (dy == 1)
            // situation = situation + 2^3;
            // if (dy == 0)
            // situation = situation + 2^4;
            // if (dy == -1)
            // situation = situation + 2^5;
            // break;
            // case(-1):
            // if (dy == 1)
            // situation = situation + 2^1;
            // if (dy == 0)
            // situation = situation + 2^8;
            // if (dy == -1)
            // situation = situation + 2^7;
            // break;
            // }
            //			

        }
        return situation;
    }

    public void move(int i) {
        int x, y;

        Graphics g = getGraphics();
        //eraseGenome(i, g);

        int situation = getSituation(i);

        Genome genome = genomeArray.get(i);
        int movement = 0;
        try {
            movement = genome.react(situation);
        } catch (Exception e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        x = genome.getX();
        y = genome.getY();

        switch (movement) {
        case (1):
            x = x - 1;
            y = y + 1;
            break;
        case (2):
            y = y + 1;
            break;
        case (3):
            x = x + 1;
            y = y + 1;
            break;
        case (4):
            x = x + 1;
            break;
        case (5):
            x = x + 1;
            y = y - 1;
            break;
        case (6):
            y = y - 1;
            break;
        case (7):
            x = x - 1;
            y = y - 1;
            break;
        case (8):
            x = x - 1;
            break;
        default:
            break;
        }

        // Vector neighbours = findNeighbours(i);

        int opponent = fieldOccupiedBy(boundaryGuard(x), boundaryGuard(y));

        if (opponent >= 0 && opponent != i) {
        	/*Field already occupied*/
            try {
                Genome opponentGenome = genome.replicate();
                opponentGenome.setX(boundaryGuard(x));
                opponentGenome.setY(boundaryGuard(y));
                genomeArray.setElementAt(opponentGenome, opponent);
                drawGenome(opponent, g);
            } catch (Exception e) {
                System.err.println(" replication error");
            }

        } else {
        	/*Nobody in the way*/
        	eraseGenome(i, g);
            genome.setX(boundaryGuard(x));
            genome.setY(boundaryGuard(y));
            drawGenome(i, g);

        }

        

    }

    public void evolveOneStep() {
        if (ConWin.verbose)
            System.out.println("..evolving");
        // Collections.shuffle(genomeArray);
        for (int i = 0; i < size; i++) {
            move(i);
            // for (int n = 0; n < size; n++)
            // {
            // Genome genome = (Genome)genomeArray.get(n);
            // System.out.print("Genome ");
            // System.out.print(n);
            // System.out.print(" x: ");
            // System.out.print(genome.getX());
            // System.out.print(" y: ");
            // System.out.println(genome.getY());
            //			
            // }
            // System.out.println("----------------------");

            // for (int t = 0; t < 100000; t++)
            // for (int q = 0; q < 10; q++);
        }

    }

    public int boundaryGuard(int position) {

        // check if the coordinates are beyond the world boundaries and if so,
        // put position back to the opposite edge

        int max = this.getDimensions();
        position = Math.min(position, max - 1);
        position = Math.max(0, position);
        return position;
    }

    @Override
    public void run() {
        _done=false;
        while (!_done) {
            evolveOneStep();
            System.out.println(++_generation);
        }
    }
    
    public void done(){
        _done=true;
    }
}
